package top.lshaci.learning.pdfqr;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.beust.jcommander.internal.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
 * PdfQrData
 *
 * @author lshaci
 * @version 1.0.3
 * @since 1.0.3
 */
@Slf4j
public class PdfQrData {

    /**
     * 用于存储PDF文档的输入流
     */
    protected InputStream pdf;

    /**
     * 存储二维码包含的内容
     */
    protected String qrContent;

    /**
     * 用于指定二维码的尺寸
     */
    protected int qrSize;

    /**
     * 用于指定二维码在PDF上的X坐标
     */
    protected float x;

    /**
     * 用于指定二维码在PDF上的Y坐标
     */
    protected float y;

    /**
     * 存储二维码位置的枚举类型
     */
    protected QrLocation qrLocation;

    /**
     * 其它的图片
     */
    private List<PdfOtherImage> otherImages;

    /**
     * 添加其它的图片
     *
     * @param otherImage 其它图片信息
     * @return PdfQrData
     */
    public PdfQrData addOtherImage(PdfOtherImage otherImage) {
        if (otherImage == null || otherImage.getBytes() == null) {
            return this;
        }
        if (StrUtil.isBlank(otherImage.getName())) {
            otherImage.setName(IdUtil.objectId() + ".png");
        }
        if (otherImages == null) {
            otherImages = Lists.newArrayList();
        }
        otherImages.add(otherImage);
        return this;
    }

    /**
     * 以ByteArrayOutputStream形式返回添加了二维码的PDF文档
     *
     * @return ByteArrayOutputStream 包含添加了二维码的PDF文档的字节数组输出流
     * @throws IOException 如果操作过程中发生I/O错误
     */
    public ByteArrayOutputStream out() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out(out);
        return out;
    }

    /**
     * 将添加了二维码的PDF文档保存到指定的文件路径
     *
     * @param saveFilePath 保存PDF文档的文件路径
     * @throws IOException 如果操作过程中发生I/O错误
     */
    public void out(String saveFilePath) throws IOException {
        out(Files.newOutputStream(Paths.get(saveFilePath)));
    }

    /**
     * 将添加了二维码的PDF文档保存到指定的File对象表示的文件
     *
     * @param saveFile 保存PDF文档的File对象
     * @throws IOException 如果操作过程中发生I/O错误
     */
    public void out(File saveFile) throws IOException {
        out(Files.newOutputStream(saveFile.toPath()));
    }

    /**
     * 将添加了二维码的PDF文档写入到指定的OutputStream
     *
     * @param out 用于写入PDF文档的OutputStream对象
     * @throws IOException 如果操作过程中发生I/O错误
     */
    public void out(OutputStream out) throws IOException {
        try (PDDocument document = PDDocument.load(pdf)) {
            // 添加二维码到PDF
            addQr(document);
            // 保存修改后的PDF
            document.save(out);
            log.debug("PDF添加二维码完成！");
        }
    }


    /**
     * 在PDF文档中添加二维码
     *
     * @param document PDDocument对象，代表要添加二维码的PDF文档
     * @throws IOException 如果文件操作失败
     */
    private void addQr(PDDocument document) throws IOException {
        // 将二维码转换为BufferedImage
        byte[] qrCodeImage = QrCodeUtil.generatePng(qrContent, qrSize, qrSize);
        // 将二维码转换为PDImageXObject
        PDImageXObject qrImage = PDImageXObject.createFromByteArray(document, qrCodeImage, "QRCode.png");

        // 获取PDF的页数并记录日志
        int numberOfPages = document.getNumberOfPages();
        log.debug("PDF页数：{}", numberOfPages);

        // 获取PDF的第一页
        PDPage page = document.getPage(0);
        PDRectangle rectangle = page.getMediaBox();
        // 解析二维码在PDF页面上的位置
        parseQrLocation(rectangle);

        // 创建一个新的内容流，用于在PDF页面上绘制内容
        PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);

        // 记录PDF页面的高度、宽度和二维码的位置
        log.debug("PDF高度：{}", rectangle.getHeight());
        log.debug("PDF宽度：{}", rectangle.getWidth());
        log.debug("二维码位置：x: {}, y:{}", x, y);

        // 在PDF页面上绘制二维码
        contentStream.drawImage(qrImage, x, y, qrSize, qrSize);
        // 在PDF页面上绘制其它图片
        drawImage(document, contentStream, rectangle);

        // 关闭内容流
        contentStream.close();
    }

    /**
     * 在PDF页面上绘制其它图片
     *
     * @param document      PDF Document对象，代表要添加图片的PDF文档
     * @param contentStream PDF PAGE CONTENT STREAM对象，用于在PDF页面上绘制内容
     * @param rectangle     PDF PAGE MEDIA BOX对象，代表PDF页面的媒体矩形
     */
    private void drawImage(PDDocument document, PDPageContentStream contentStream, PDRectangle rectangle) throws IOException {
        if (CollUtil.isEmpty(otherImages)) {
            return;
        }
        for (PdfOtherImage image : otherImages) {
            PDImageXObject imageObject = PDImageXObject.createFromByteArray(document, image.getBytes(), image.getName());
            contentStream.drawImage(imageObject,
                    image.getXfunc().apply(rectangle.getWidth()), image.getYfunc().apply(rectangle.getHeight()),
                    image.getWidth(), image.getHeight()
            );
        }
    }

    /**
     * 解析二维码位置
     *
     * @param rectangle PDF页面的媒体矩形
     */
    private void parseQrLocation(PDRectangle rectangle) {
        if (qrLocation == null) {
            return;
        }
        int[] location = qrLocation.parseLocation(rectangle, qrSize);
        x = location[0];
        y = location[1];
    }
}
